//房间模块
var roomMgr = require("./roommgr");
//用户模块
var userMgr = require("./usermgr");
//公共模块
var mjutils = require('./mjutils');
//数据库模块
var db = require("../utils/db");
//公共函数
var utilfunc = require("../utils/util_func");

/***********************全局定义******************/
//游戏数据
var games = {};
//座位数据
var gameSeatsOfUsers = {};
//解散列表
var dissolvingList = [];
//财神牌
var caishenPai = 0;
//白板牌
var baibanPai = 29;
//最小风牌(包括中发白)
var minFengPai = 27;
//最大风牌(包括中发白)
var maxFengPai = 33;
//任意牌(任意一张牌来了都胡)
var anyHuPai = 34;
//动作枚举
var ACTION_CHUPAI = 1;
var ACTION_MOPAI = 2;
var ACTION_PENG = 3;
var ACTION_GANG = 4;
var ACTION_HU = 5;
var ACTION_ZIMO = 6;
var ACTION_CHI = 7;

//开始新的一局
exports.begin = function(roomId) {
    //房间数据
    var
    roomInfo = roomMgr.getRoom(roomId);
    if(roomInfo == null){
        return;
    }
    //随机财神
    caishenPai = Math.floor(Math.random() * 34);
    //游戏数据
    var game = {
        conf:roomInfo.conf,                 //房间配置
        roomInfo:roomInfo,                  //房间信息
        gameIndex:roomInfo.numOfGames,      //局数
        button:roomInfo.nextButton,         //庄家?
        mahjongs:new Array(136),            //麻将集合
        currentIndex:0,                     //摸牌索引
        gameSeats:new Array(4),             //座位信息
        numOfQue:0,                         //缺一门数
        turn:0,                             //轮到某某
        chuPai:-1,                          //当前出牌
        state:"idle",                       //游戏状态
        firstHupai:-1,                      //先胡位置索引
        yipaoduoxiang:-1,                   //一炮多响位置索引
        fangpaoshumu:-1,                    //放炮数目
        actionList:[],                      //操作记录
        hupaiList:[],                       //胡牌列表
        chupaiCnt:0,                        //出牌次数
        fengpaiList:[],                     //出风牌列表
    };
    //局数
    roomInfo.numOfGames++;
    //初始化座位
    var seats = roomInfo.seats;
    for(var i = 0; i < 4; ++i){
        //座位数据
        var data = game.gameSeats[i] = {};
        //游戏数据
        data.game = game;
        //座位索引
        data.seatIndex = i;
        //玩家标识
        data.userId = seats[i].userId;
        //持有的牌
        data.holds = [];
        //打出的牌
        data.folds = [];
        //暗杠的牌
        data.angangs = [];
        //点杠的牌
        data.diangangs = [];
        //弯杠的牌
        data.wangangs = [];
        //碰了的牌
        data.pengs = [];
        //吃了的牌
        data.chis = [];
        //供选择的吃牌信息
        data.canchis = [];
        //玩家手上的牌的数目，用于快速判定碰杠
        data.countMap = {};
        //玩家听牌，用于快速判定胡了的番数
        data.tingMap = {};
        data.pattern = "";
        //是否可以吃
        data.canChi = false;
        //是否可以杠
        data.canGang = false;
        //用于记录玩家可以杠的牌
        data.gangPai = [];
        //是否可以碰
        data.canPeng = false;
        //是否可以胡
        data.canHu = false;
        //是否可以出牌
        console.log("game turn is "+game.button);
        if (i==game.button)
            data.canChuPai = true;
        else
            data.canChuPai = false;
        //如果guoHuFan >=0 表示处于过胡状态，
        //如果过胡状态，那么只能胡大于过胡番数的牌
        data.guoHuFan = -1;
        //是否胡了
        data.hued = false;
        //是否是自摸
        data.iszimo = false;
        //杠胡
        data.isGangHu = false;
        //动作
        data.actions = [];
        //番数
        data.fan = 0;
        //分数
        data.score = 0;
        //上次番杠座位
        data.lastFangGangSeat = -1;
        //优先出的风牌
        data.youxianChuFengPai = [];
        //座位数据
        gameSeatsOfUsers[data.userId] = data;
    }
    console.log("button is "+game.button);
    //存储每局游戏数据
    games[roomId] = game;
    //洗牌
    shuffle(game);
    //发牌
    deal(game);
    //剩多少张牌
    var numOfMJ = game.mahjongs.length - game.currentIndex;
    //游戏开始通知
    for(var i = 0; i < seats.length; ++i){
        //开局时，通知前端必要的数据
        var s = seats[i];
        //发送财神
        userMgr.sendMsg(s.userId,'game_init_caishen',caishenPai);
        //通知玩家手牌
        userMgr.sendMsg(s.userId,'game_holds_push',game.gameSeats[i].holds);
        //优先出的风牌
        if (i==game.button)
        {
            //找出先出的风牌
            findoutYouXianFengPai(game,game.gameSeats[i]);
            if (game.gameSeats[i].youxianChuFengPai.length>0)
            {
                userMgr.sendMsg(s.userId,'game_youxian_push',game.gameSeats[i].youxianChuFengPai);
            }
        }
        //通知还剩多少张牌
        userMgr.sendMsg(s.userId,'mj_count_push',numOfMJ);
        //通知还剩多少局
        userMgr.sendMsg(s.userId,'game_num_push',roomInfo.numOfGames);
        //通知游戏开始
        userMgr.sendMsg(s.userId,'game_begin_push',game.button);
    }
};

//获取麻将类型
function getMJType(id)
{
    if(id >= 0 && id < 9)
    {
        //筒
        return 0;
    }
    else if(id >= 9 && id < 18)
    {
        //条
        return 1;
    }
    else if(id >= 18 && id < 27)
    {
        //万
        return 2;
    }
    else if (id >=27 && id < 30)
    {
        //中 发 白
        return 3;
    }
    else if (id >= 30 && id <34)
    {
        //风
        return 4;
    }
}

//洗牌
function shuffle(game) 
{
    var mahjongs = game.mahjongs;
    //筒 0 ~ 8 表示筒子,条 9 ~ 17表示条子,万 18 ~ 26表示万,风 27 ~ 29表示中、发、白,30~33表示风
    var index = 0;
    for(var i = 0; i <= maxFengPai; ++i){
        for(var c = 0; c < 4; ++c){
            mahjongs[index] = i;
            index++;
        }
    }
    //洗牌
    for(var i = 0; i < mahjongs.length; ++i){
        var lastIndex = mahjongs.length - 1 - i;
        var index = Math.floor(Math.random() * lastIndex);
        var t = mahjongs[index];
        mahjongs[index] = mahjongs[lastIndex];
        mahjongs[lastIndex] = t;
    }
}

//发牌
function deal(game)
{
    //强制清0
    game.currentIndex = 0;
    //每人16张 一共 16*4 ＝ 64张 庄家多一张 65张
    var seatIndex = game.button;
    for(var i = 0; i < 64; ++i){
        var mahjongs = game.gameSeats[seatIndex].holds;
        if(mahjongs == null){
            mahjongs = [];
            game.gameSeats[seatIndex].holds = mahjongs;
        }
        mopai(game,seatIndex);
        seatIndex++;
        seatIndex %= 4;
    }
    //庄家多摸最后一张
    mopai(game,game.button);

    //设置庄家
    game.turn = game.button;
}
//摸牌
function mopai(game,seatIndex) 
{
    if(game.currentIndex == game.mahjongs.length-8)
    {
        return -1;
    }
    var data = game.gameSeats[seatIndex];
    var mahjongs = data.holds;
    var pai = game.mahjongs[game.currentIndex];
    mahjongs.push(pai);
    
    //统计牌的数目 ，用于快速判定（空间换时间）
    var c = data.countMap[pai];
    if(c == null) {
        c = 0;
    }
    data.countMap[pai] = c + 1;
    game.currentIndex++;
    return pai;
}
//找出风牌
function findoutYouXianFengPai(game,seatData)
{
    //根据已出牌定出的风牌
    seatData.youxianChuFengPai.length = 0;
    if (game.fengpaiList.length>0)
    {
        for (var i=game.fengpaiList.length-1; i>=0; --i)
        {
            var pai = game.fengpaiList[i];
            if (seatData.countMap[pai]==1)
            {
                seatData.youxianChuFengPai.push(pai);
                break;
            }
        }
    }

    //没有出牌找风牌中的单牌
    if (seatData.youxianChuFengPai.length<=0)
    {
        for (var key in seatData.countMap)
        {
            var pai = parseInt(key);
            var paiNum = seatData.countMap[key];
            if (pai>=minFengPai && pai<=maxFengPai && paiNum==1)
            {
                if (pai==caishenPai)
                {
                    continue;
                }
                if (pai != baibanPai) //不是白板牌的风牌可以先出
                {
                    seatData.youxianChuFengPai.push(pai);
                }
                else if (caishenPai!=baibanPai && caishenPai>=minFengPai && caishenPai<=maxFengPai) //白板代替除白板本身以外的风牌,优先出
                {
                    seatData.youxianChuFengPai.push(pai);
                }
            }
        }
    }
}
//下一个摸牌
function nextUserMoPai(game)
{
    game.chuPai = -1;
    var turnSeat = game.gameSeats[game.turn];
    turnSeat.lastFangGangSeat = -1;
    turnSeat.guoHuFan = -1;
    var pai = mopai(game,game.turn);
    if(pai == -1)
    {
        //牌摸完了，结束
        doGameOver(game,turnSeat.userId);
        return;
    }
    else
    {
        var numOfMJ = game.mahjongs.length - game.currentIndex;
        userMgr.broadcastInRoom('mj_count_push',numOfMJ,turnSeat.userId,true);
    }
    recordGameAction(game,game.turn,ACTION_MOPAI,pai);

    //通知前端新摸的牌
    userMgr.sendMsg(turnSeat.userId,'game_mopai_push',pai);

    //通知优先出的牌
    findoutYouXianFengPai(game,turnSeat);
    if (turnSeat.youxianChuFengPai.length>0)
    {
        userMgr.sendMsg(turnSeat.userId,'game_youxian_push',turnSeat.youxianChuFengPai);
    }

    //检查是否可以暗杠或者胡
    checkCanAnGang(game,turnSeat);

    //检查胡，直杠，弯杠
    checkCanWanGang(game,turnSeat,pai);

    //检查看是否可以和
    checkCanHu(game,turnSeat,pai);

    //广播通知玩家出牌方
    turnSeat.canChuPai = true;
    userMgr.broadcastInRoom('game_chupai_push',turnSeat.userId,turnSeat.userId,true);

    //通知玩家做对应操作
    sendOperations(game,turnSeat,game.chuPai);
}
//出牌
exports.chuPai = function(userId,pai)
{
    //其中一家出的牌
    pai = Number.parseInt(pai);
    console.log("follow is chupai data: ");
    console.log(pai);
    if(pai < 0 || pai == caishenPai)
    {
        console.log("pai data is wrong.");
        return;
    }
    //座位数据
    var seatData = gameSeatsOfUsers[userId];
    if(seatData == null)
    {
        console.log("can't find user game data.");
        return;
    }
    //如果不该他出，则忽略
    var game = seatData.game;
    var seatIndex = seatData.seatIndex;
    if(game.turn != seatData.seatIndex)
    {
        console.log("not your turn.");
        return;
    }
    //已胡
    if(seatData.hued){
        console.log('you have already hued. no kidding plz.');
        return;
    }
    //当前不能出牌
    if(seatData.canChuPai == false)
    {
        console.log('no need chupai.');
        return;
    }

    //从此人牌中扣除
    var index = seatData.holds.indexOf(pai);
    if(index == -1)
    {
        console.log("holds:" + seatData.holds);
        console.log("chuPai: can't find mj." + pai);
        return;
    }
    seatData.holds.splice(index,1);
    seatData.countMap[pai] --;
    //记录出牌
    seatData.canChuPai = false;
    game.chupaiCnt ++;
    seatData.guoHuFan = -1;
    game.chuPai = pai;
    recordGameAction(game,seatData.seatIndex,ACTION_CHUPAI,pai);
    if (pai>=minFengPai && pai<=maxFengPai)
    {
        if (pai != baibanPai) //不是白板牌的风牌可以先出
        {
            game.fengpaiList.push(pai);
        }
        else if (caishenPai!=baibanPai && caishenPai>=minFengPai && caishenPai<=maxFengPai) //白板代替除白板本身以外的风牌,优先出
        {
            game.fengpaiList.push(pai);
        }
    }
    //是否听牌
    checkCanTingPai(game,seatData);
    //广播出牌通知
    userMgr.broadcastInRoom('game_chupai_notify_push',{userId:seatData.userId,pai:pai},seatData.userId,true);
    //检查是否有人要胡，要碰 要杠
    var hasActions = false;
    for(var i = 0; i < game.gameSeats.length; ++i)
    {
        //玩家自己不检查
        if(game.turn == i)
        {
            continue;
        }
        //已经和牌的不再检查
        var seatInfo = game.gameSeats[i];
        if(seatInfo.hued)
        {
            continue;
        }
        //上家的牌才能吃
        if ((game.turn == 3 && i==0) || (game.turn<3 && i==game.turn+1))
        {
            checkCanChiPai(seatInfo,pai);
        }
        //是否能胡
        checkCanHu(game,seatInfo,pai);
        if(seatData.lastFangGangSeat >= 0)
        {
            if(seatInfo.canHu && seatInfo.guoHuFan >= 0 && seatInfo.tingMap[pai].fan <= seatInfo.guoHuFan)
            {
                console.log("ddd.guoHuFan:" + seatInfo.guoHuFan);
                seatInfo.canHu = false;
                userMgr.sendMsg(seatInfo.userId,'guohu_push');            
            }     
        }
        //是否能碰
        checkCanPeng(game,seatInfo,pai);
        //是否点杠
        checkCanDianGang(game,seatInfo,pai);
        //是否能吃、碰、杠、胡
        if(hasOperations(seatInfo))
        {
            sendOperations(game,seatInfo,game.chuPai);
            hasActions = true;

            //发送优先出牌
            if(seatInfo.canGang || seatInfo.canPeng || seatInfo.canChi)
            {
                findoutYouXianFengPai(game,seatInfo);
                if (seatInfo.youxianChuFengPai.length>0)
                {
                    userMgr.sendMsg(seatInfo.userId,'game_youxian_push',seatInfo.youxianChuFengPai);
                }
            }
        }
    }
    //如果没有人有操作，则向下一家发牌，并通知他出牌
    if(!hasActions)
    {
        //0.5秒后通知下家摸牌(发牌)
        setTimeout(function(){
            userMgr.broadcastInRoom('guo_notify_push',{userId:seatData.userId,pai:game.chuPai},seatData.userId,true);
            seatData.folds.push(game.chuPai);
            game.chuPai = -1;
            moveToNextUser(game);
            nextUserMoPai(game);
        },500);
    }
};
//保存吃牌
function saveChiPaiInfo(seatData,firstPai,secondPai,thirdPai)
{
    seatData.canchis.push(firstPai);
    seatData.canchis.push(secondPai);
    seatData.canchis.push(thirdPai);
}
//检查是否可以吃
function checkCanChiPai(seatData,targetPai)
{
    //风牌和财神不参与吃牌
    if (targetPai>=minFengPai || caishenPai==targetPai)
    {
        return;
    }
    seatData.canchis.length = 0;

    //财神不能打出
    if (targetPai%9==0 
        && targetPai+1 != caishenPai
        && seatData.countMap[targetPai+1]>0
        && targetPai+2 != caishenPai
        && seatData.countMap[targetPai+2]>0)
    {
        seatData.canChi = true;
        saveChiPaiInfo(seatData,targetPai,targetPai+1,targetPai+2);
    }
    if (targetPai%9>1 
        && targetPai%9<7
        && targetPai-1 != caishenPai
        && seatData.countMap[targetPai-1]>0
        && targetPai-2 != caishenPai
        && seatData.countMap[targetPai-2]>0)
    {
        seatData.canChi = true;
        saveChiPaiInfo(seatData,targetPai-2,targetPai-1,targetPai);
    }
    if (targetPai%9>1 
        && targetPai%9<7
        && targetPai+1 != caishenPai
        && seatData.countMap[targetPai+1]>0
        && targetPai+2 != caishenPai
        && seatData.countMap[targetPai+2]>0)
    {
        seatData.canChi = true;
        saveChiPaiInfo(seatData,targetPai,targetPai+1,targetPai+2);
    }
    if (targetPai%9>0 
        && targetPai%9<8
        && targetPai-1 != caishenPai
        && seatData.countMap[targetPai-1]>0
        && targetPai+1 != caishenPai
        && seatData.countMap[targetPai+1]>0)
    {
        seatData.canChi = true;
        saveChiPaiInfo(seatData,targetPai-1,targetPai,targetPai+1);
    }
    if (targetPai%9==8
        && targetPai-1 != caishenPai
        && seatData.countMap[targetPai-1]>0
        && targetPai-2 != caishenPai
        && seatData.countMap[targetPai-2]>0)
    {
        seatData.canChi = true;
        saveChiPaiInfo(seatData,targetPai-2,targetPai-1,targetPai);
    }

    //只有财神不是白板时，白板才能参与吃操作
    if (caishenPai != baibanPai && seatData.countMap[baibanPai] > 0)
    {
        if (targetPai%9 == 0
            && caishenPai == targetPai+1 
            && seatData.countMap[targetPai+2]>0)
            {
                seatData.canChi = true;
                saveChiPaiInfo(seatData,targetPai,baibanPai,targetPai+2);
            }
        if (targetPai%9 == 0 
            && caishenPai == targetPai+2
            && seatData.countMap[targetPai+1] > 0)
            {
                seatData.canChi = true;
                saveChiPaiInfo(seatData,targetPai,targetPai+1,baibanPai);
            }
        if (targetPai%9 > 0 && targetPai%9 < 8
            && caishenPai == (targetPai-1)
            && seatData.countMap[targetPai+1] > 0)
            {
                seatData.canChi = true;
                saveChiPaiInfo(seatData,baibanPai,targetPai,targetPai+1);
            }
        if (targetPai%9 > 0 && targetPai%9 < 8
            && caishenPai == (targetPai+1)
            && seatData.countMap[targetPai-1] > 0)
            {
                seatData.canChi = true;
                saveChiPaiInfo(seatData,targetPai-1,targetPai,baibanPai);
            }
        if (targetPai%9==8
            && caishenPai == targetPai-1
            && seatData.countMap[targetPai-2] > 0)
            {
                seatData.canChi = true;
                saveChiPaiInfo(seatData,targetPai-2,baibanPai,targetPai);
            }
        if (targetPai%9==8
            && caishenPai == targetPai-2
            && seatData.countMap[targetPai-1] > 0)
            {
                seatData.canChi = true;
                saveChiPaiInfo(seatData,baibanPai,targetPai-1,targetPai);
            }
     }
}

//检查是否可以碰
function checkCanPeng(game,seatData,targetPai) 
{
    //财神不参与碰牌,只在胡牌时随意组合
    if (targetPai==caishenPai) 
    {
        return;
    }

    //打印
    console.log("userId: " +seatData.userId)
    console.log("check peng have holds: "+seatData.holds);
    console.log("check peng have countMap: "+seatData.countMap);

    //只有财神不是白板时，白板才能参与碰操作
    if (caishenPai!=baibanPai && targetPai==baibanPai && seatData.countMap[baibanPai]==2)
    {
        seatData.canPeng = true;
        return;
    }

    //除了财神外,根据个数判断
    var count = seatData.countMap[targetPai];
    if(count != null && count >= 2)
    {   
        seatData.canPeng = true;
    }
}

//检查是否可以点杠
function checkCanDianGang(game,seatData,targetPai)
{
    //如果没有牌了，则不能再杠
    if(game.mahjongs.length <= game.currentIndex)
    {
        return;
    }
    //财神不参与杠牌,只在杠牌时随意组合
    if (targetPai==caishenPai) 
    {
        return;
    }
    //校验是否牌错误
    var count = seatData.countMap[targetPai];
    if (count == 4)
    {
        return;
    }
    //财神不是白板
    if (caishenPai!=baibanPai && targetPai==baibanPai && seatData.countMap[baibanPai]==3)
    {
        seatData.canGang = true;
        seatData.gangPai.push(targetPai);
        return;
    }
    if(count != null && count >= 3)
    {
        seatData.canGang = true;
        seatData.gangPai.push(targetPai);
    }
}

//检查是否可以暗杠
function checkCanAnGang(game,seatData)
{
    //如果没有牌了，则不能再杠
    if(game.mahjongs.length <= game.currentIndex){
        return;
    }

    //循环找出暗杠牌
    for(var key in seatData.countMap)
    {
        var pai = parseInt(key);
        var paiNum = seatData.countMap[key];

        //财神不是白板
        if (caishenPai!=baibanPai && pai==baibanPai && seatData.countMap[baibanPai]==4)
        {
            seatData.canGang = true;
            seatData.gangPai.push(pai);
            continue;
        }
        if(paiNum != null && paiNum == 4)
        {
            seatData.canGang = true;
            seatData.gangPai.push(pai);
        }
    }
}

//检查是否可以弯杠(自己摸起来的时候)
function checkCanWanGang(game,seatData)
{
    //如果没有牌了，则不能再杠
    if(game.mahjongs.length <= game.currentIndex)
    {
        return;
    }

    //从碰过的牌中选
    for(var i = 0; i < seatData.pengs.length; ++i)
    {
        var pai = seatData.pengs[i];
        if(seatData.countMap[pai] == 1)
        {
            if (caishenPai == baibanPai && pai == baibanPai)
            {
                console.log('ErrMsg: when caishenpai is baiban, you can not gang!')
                continue;
            }
            seatData.canGang = true;
            seatData.gangPai.push(pai);
        }
    }
}

//检查是否能胡
function checkCanHu(game,seatData,targetPai) 
{
    game.lastHuPaiSeat = -1;
    seatData.canHu = false;
    //有3个财神且有符合基础0倍牌型或软8对的牌型
    if (seatData.countMap[caishenPai] == 3)
    {
        //是否符合软8对
        seatData.canHu = true;
        return;
    }
    if (targetPai==anyHuPai)
    {
        seatData.canHu = true;
        return;
    }
    for(var k in seatData.tingMap)
    {
        if(targetPai == k || targetPai==caishenPai)
        {
            seatData.canHu = true;
            break;
        }
        if (k==caishenPai)
        {
            seatData.canHu = true;
            break;
        }
    }
}

//清除状态
function clearAllOptions(game,seatData){
    var fnClear = function(sd){
        sd.canChi = false;
        sd.canPeng = false;
        sd.canGang = false;
        sd.gangPai = [];
        sd.canHu = false;
        sd.lastFangGangSeat = -1;    
    }
    if(seatData){
        fnClear(seatData);
    }
    else{
        game.qiangGangContext = null;
        for(var i = 0; i < game.gameSeats.length; ++i){
            fnClear(game.gameSeats[i]);
        }
    }
}

//检查听牌
function checkCanTingPai(game,seatData)
{
    seatData.tingMap = {};

    //胡牌说明
    /*
    概念说明:
    软牌：由财神搭牌的胡牌算软牌 0倍  搭牌：由财神和别的牌组合而成
    硬牌：无财神搭牌的胡牌算硬牌 1倍
    软8对：AA,AA,AA,AA,AA,AA,AA,AA（1个财神搭牌,牌算硬牌1倍）。1倍
    硬8对: AA,AA,AA,AA,AA,AA,AA,AA（没财神搭牌, 牌算硬牌1倍）。1倍

    1、基础牌型： 
    a. AA（牛），ABC，ABC，ABC，ABC，ABC。 
    b. AA（牛），ABC，ABC，ABC，ABC，AAA。 
    c. AA（牛），ABC，ABC，ABC，AAA，AAA。 
    d. AA（牛），ABC，ABC，AAA，AAA，AAA。 
    e. AA（牛），ABC，AAA，AAA，AAA，AAA。 

    2、1倍牌型:
        f. AA,AA,AA,AA,AA,AA,AA,AA,B．（软8对：8对有2个财神的，胡牌算硬牌）。   1倍
        g.软8对种含有3个财神但没有符合基础牌型(不包含碰碰胡)，也可胡牌，算硬牌。   1倍
        h.没有软8对,有3个财神但没有符合基础牌型(不包含碰碰胡) ，也可胡牌，算硬牌。 1倍

    3、双翻牌型:
        a.AA,AA,AA,AA,AA,AA,AA,AA,B．（硬8对：没财神搭牌的，胡牌算双翻）。
        注：(1) 8对没财神,算双翻,没搭牌 2倍
            (2)一个财神作B的话，算双翻,没搭牌. 2倍
            (3)如果有两个财神也算双翻,没搭牌.  2倍
        如： AA,AA,AA,AA,AA,AA,AA,A财神+ 财神
            AA,AA,AA,AA,AA,AA,AA,AA+ 财神

        b.有3个财神且有符合基础牌型或软8对的牌型。 
        c.天胡：在庄家抓到第一张牌时就胡了。
        d.地胡：闲家没有抓过牌，别人放冲胡；或者自摸。
        e.单吊：经过吃 或 碰 或 杠后，在手上的牌只剩最后一张时胡的。 
        f.全球神：当手上只剩四张牌的时候，其中有张牌是财神，吃掉或碰掉或杠掉（不含财神）后打出去的那张牌没有被其他人胡掉的话（最后一张手上拿的是财神），这个人就可以赢了。（全球神时，其它玩家不能摸牌了）
        g.碰碰胡：牌型符合AA，AAA，AAA，AAA，AAA，AAA（AAA数=5，包括碰杠）
    */

    //无财神,无白板情况下是否听牌
    if ((seatData.countMap[caishenPai]==null || seatData.countMap[caishenPai]<=0) 
        && (seatData.countMap[baibanPai]==null || seatData.countMap[baibanPai]<=0))
    {
        if (seatData.holds.length>0)
        {
            //剩一张手牌,单吊
            if (seatData.holds.length==1)
            {
                checkCanTingPaiByNormal(seatData);
                if (seatData.tingMap[seatData.holds[0]]==null || seatData.tingMap[seatData.holds[0]]==0)
                {
                    seatData.tingMap[seatData.holds[0]] = {fan:1,pattern:"hardnormal"};
                    console.log("dan diao not find default");
                }
            }

            //硬8对,双翻牌型4倍
            var type = noCaiShenIs8Pairs(seatData);
            if (1==type)
            {
                seatData.tingMap[anyHuPai] = {fan:2,pattern:"hard8pairs"};
            }
            
            //检查是否碰碰胡
            //if (5==(mjutils.matchPengNum(seatData)+seatData.pengs.length+seatData.diangangs.length+seatData.wangangs.length+seatData.angangs.length))
            {
                //seatData.tingMap[seatData.holds[0]] = {fan:2,pattern:"hardpengpenghu"};
            }
            //else //检查是不是平胡
            {
                console.log("no caishen and baiban check whether is hard normal hu pai");               
                mjutils.checkTingPai(seatData,0,9);        
                mjutils.checkTingPai(seatData,9,18);        
                mjutils.checkTingPai(seatData,18,27);
                mjutils.checkTingPai(seatData,27,34); 
            }
        }
    }
    else if ((seatData.countMap[caishenPai]==null || seatData.countMap[caishenPai]<=0) && seatData.countMap[baibanPai]>0)  //无财神,有白板情况下是否听牌
    {
        if (caishenPai==baibanPai) //白板就是财神
        {
            console.log("ErrMsg: caishen is baiban,countMap data is wrong!");
            return;
        }
        else //白板充当替代牌，替代财神牌
        {
            if (seatData.holds.length>0)
            {
                //剩一张手牌,单吊
                if (seatData.holds.length==1)
                {
                    checkCanTingPaiByNormal(seatData);
                    if (seatData.tingMap[seatData.holds[0]]==null || seatData.tingMap[seatData.holds[0]]==0)
                    {
                        seatData.tingMap[seatData.holds[0]] = {fan:1,pattern:"hardnormal"};
                        console.log("dan diao not find default");
                    }
                }

                //硬8对,双翻牌型4倍
                var type = noCaiShenIs8Pairs(seatData);
                if (1==type)
                {
                    seatData.tingMap[anyHuPai] = {fan:2,pattern:"hard8pairs"};
                }

                //检查是否碰碰胡
                //if (5==(mjutils.matchPengNum(seatData)+seatData.pengs.length+seatData.diangangs.length+seatData.wangangs.length+seatData.angangs.length))
                {
                    //seatData.tingMap[seatData.holds[0]] = {fan:2,pattern:"hardpengpenghu"};
                }
               // else //检查是否含有白板的平胡,把白板赋值为财神牌值
                {
                    console.log("no caishen but have baiban check whether is hard normal hu pai");
                    var baibanCnt=seatData.countMap[baibanPai];
                    seatData.countMap[caishenPai]+=baibanCnt;
                    seatData.countMap[baibanPai]=0;
                    mjutils.checkTingPai(seatData,0,9);        
                    mjutils.checkTingPai(seatData,9,18);        
                    mjutils.checkTingPai(seatData,18,27); 
                    seatData.countMap[baibanPai]=baibanCnt;
                    seatData.countMap[caishenPai]-=baibanCnt;
                }
            }
        }
    }
    else if (seatData.countMap[caishenPai]>0 && seatData.countMap[caishenPai]<=2 && (seatData.countMap[baibanPai]==null || seatData.countMap[baibanPai]<=0))  //有财神,无白板情况下是否听牌
    {
        if (caishenPai==baibanPai) //白板就是财神
        {
            console.log("ErrMsg: caishen is baiban,countMap data is wrong!");
            return;
        }
        else
        {
            if (seatData.holds.length>0)
            {
                //剩一张手牌,单吊
                if (seatData.holds.length==1)
                {
                    checkCaningPaiByCaiShen(seatData);
                    if (seatData.tingMap[seatData.holds[0]]==null || seatData.tingMap[seatData.holds[0]]==0)
                    {
                        seatData.tingMap[seatData.holds[0]] = {fan:1,pattern:"hardnormal"};
                        console.log("dan diao not find default");
                    }
                }

                //有财神的软8对与硬8对
                if (is8PairsWithCaiShen(seatData)==1)
                {
                    seatData.tingMap[seatData.holds[0]] = {fan:1,pattern:"soft8pairs"};
                }
                else if (is8PairsWithCaiShen(seatData)==2)
                {
                    seatData.tingMap[seatData.holds[0]] = {fan:2,pattern:"hard8pairs"};
                }

                //检查是否碰碰胡
                //if (5==(mjutils.matchPengNum(seatData)+seatData.pengs.length+seatData.diangangs.length+seatData.wangangs.length+seatData.angangs.length))
                {
                    //seatData.tingMap[seatData.holds[0]] = {fan:1,pattern:"softpengpenghu"};
                }
                //else //检查是否含有财神的胡牌
                {
                    console.log("have caishen check whether is hard normal hu pai");
                    var caishenCnt = seatData.countMap[caishenPai]; 
                    var paiCnt = 0;
                    if (caishenCnt==1)
                    {
                        mjutils.checkTingPai(seatData,0,9);        
                        mjutils.checkTingPai(seatData,9,18);        
                        mjutils.checkTingPai(seatData,18,27); 
                        while(!isTinged(seatData))
                        {
                            if (paiCnt != caishenPai)
                            {
                                seatData.countMap[paiCnt] += seatData.countMap[caishenPai];
                                seatData.countMap[caishenPai] = 0;
                                mjutils.checkTingPai(seatData,0,9);        
                                mjutils.checkTingPai(seatData,9,18);        
                                mjutils.checkTingPai(seatData,18,27);
                                seatData.countMap[caishenPai] = caishenCnt;
                                seatData.countMap[paiCnt] -= seatData.countMap[caishenPai];
                                
                                if (paiCnt==anyHuPai-1)
                                {
                                    break;
                                } 
                            }
                            ++paiCnt;
                        }
                        if (isTinged(seatData))
                        {
                            console.log("you has ting pai,target pai");
                            return;
                        }
                    }
                }
            }
        }
    }
    else if (seatData.countMap[caishenPai]>0 && seatData.countMap[caishenPai]<=2 && seatData.countMap[baibanPai]>0 && seatData.countMap[baibanPai]<=2)   //有财神，有白板情况下是否听牌
    {
        //剩一张手牌,单吊
        if (seatData.holds.length==1)
        {
            checkCanTingPaiByCaiShen(seatData);
            if (seatData.tingMap[seatData.holds[0]]==null || seatData.tingMap[seatData.holds[0]]==0)
            {
                seatData.tingMap[seatData.holds[0]] = {fan:1,pattern:"hardnormal"};
                console.log("dan diao not find default");
            }
        }

        //有财神白板的软8对与硬8对
        if (is8PairsWithCaiShenBaiBan(seatData)==1)
        {
            seatData.tingMap[seatData.holds[0]] = {fan:1,pattern:"soft8pairs"};
        }
        else if (is8PairsWithCaiShenBaiBan(seatData)==2)
        {
            seatData.tingMap[seatData.holds[0]] = {fan:2,pattern:"hard8pairs"};
        }

        //检查是否碰碰胡
        //if (5==(mjutils.matchPengNum(seatData)+seatData.pengs.length+seatData.diangangs.length+seatData.wangangs.length+seatData.angangs.length))
        {
            //seatData.tingMap[seatData.holds[0]] = {fan:1,pattern:"softpengpenghu"};
        }
        //else //检查是否含有财神白板的胡牌
        {
            console.log("have caishen and baiban check whether is hard normal hu pai");               
            mjutils.checkTingPai(seatData,0,9);        
            mjutils.checkTingPai(seatData,9,18);        
            mjutils.checkTingPai(seatData,18,27);  
        }
    }
}

//没有财神组合情况下, 是否能听牌了 (附加说明: 以下函数只能用于单吊剩一张牌的情况下)
function checkCanTingPaiByNormal(seatData)
{
    //手牌校验
    if (seatData.holds.length==0)
    {
        return;
    }
    //听的那张牌
    if (seatData.countMap[caishenPai]==null && caishenPai != baibanPai)
    {
        checkCanTingByOneLeft(seatData,seatData.holds[0],1,2,"hardnormal","hardpengpenghu");
    }
}

//有财神组合情况下, 是否能听牌了(附加说明: 以下函数只能用于单吊剩一张牌的情况下)
function checkCanTingPaiByCaiShen(seatData)
{
    //手牌校验
    if (seatData.holds.length==0)
    {
        return;
    }
    //听的那张牌
    if (seatData.countMap[caishenPai]>0 && caishenCnt==1)
    {
        checkCanTingByOneLeft(seatData,caishenPai,0,1,"softnormal","softpengpenghu");
    }
}

//根据单张剩余判断是否听牌
function checkCanTingByOneLeft(seatData,tingPai,normalFan,pengpengFan,patternVal1,patternVal2)
{
    //单吊剩一张牌
    if ((seatData.chis.length/3)==5)
    {
        seatData.tingMap[tingPai] = {fan:normalFan,pattern:patternVal1};
    }
    else if (seatData.pengs.length==1 && (seatData.chis.length/3)==4)
    {
        seatData.tingMap[tingPai] = {fan:normalFan,pattern:patternVal1};
    }
    else if (seatData.pengs.length==2 && (seatData.chis.length/3)==3)
    {
        seatData.tingMap[tingPai] = {fan:normalFan,pattern:patternVal1};
    }
    else if (seatData.pengs.length==3 && (seatData.chis.length/3)==2)
    {
        seatData.tingMap[tingPai] = {fan:normalFan,pattern:patternVal1};
    }
    else if (seatData.pengs.length==4 && (seatData.chis.length/3)==1)
    {
        seatData.tingMap[tingPai] = {fan:normalFan,pattern:patternVal1};
    }
    else if (seatData.pengs.length==5) 
    {
        seatData.tingMap[tingPai] = {fan:pengpengFan,pattern:patternVal2};
    }
    else if ((seatData.angangs.length+seatData.diangangs.length+seatData.wangangs.length)==5)
    {
        seatData.tingMap[tingPai] = {fan:pengpengFan,pattern:patternVal2};
    }
    else if ((seatData.angangs.length+seatData.diangangs.length+seatData.wangangs.length)==4 && seatData.pengs.length==1)
    {
        seatData.tingMap[tingPai] = {fan:pengpengFan,pattern:patternVal2};
    }
    else if ((seatData.angangs.length+seatData.diangangs.length+seatData.wangangs.length)==3 && seatData.pengs.length==2)
    {
        seatData.tingMap[tingPai] = {fan:pengpengFan,pattern:patternVal2};
    }
    else if ((seatData.angangs.length+seatData.diangangs.length+seatData.wangangs.length)==2 && seatData.pengs.length==3)
    {
        seatData.tingMap[tingPai] = {fan:pengpengFan,pattern:patternVal2};
    }
    else if ((seatData.angangs.length+seatData.diangangs.length+seatData.wangangs.length)==1 && seatData.pengs.length==4)
    {
        seatData.tingMap[tingPai] = {fan:pengpengFan,pattern:patternVal2};
    }
    console.log("dan diao ting pai == "+seatData.tingMap[tingPai]);
}
//没财神时,检查是否8对, 返回结果 0--不是8对,1--8对
function noCaiShenIs8Pairs(seatData)
{
    if (seatData.countMap[caishenPai]>0)
    {
        return -1;
    }
    var pairCnt = 0;
    if (seatData.holds.length==16)
    {
        for (var key in seatData.countMap)
        {
            var pai = Number.parseInt(key);
            if (seatData.countMap[pai] == 2)
            {
                ++pairCnt;
            }
        }
    }
    if (pairCnt==8)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

//有财神没白板情况下是否8对,0--没8对，1--软8对，2--硬8对
function is8PairsWithCaiShen(seatData)
{
    if (seatData.countMap[caishenPai]==null)
    {
        console.log("ErrMsg: data is wrong, no caishen");
        return -1;
    }
    var pairCnt = 0;
    if (seatData.holds.length==16)
    {
        for (var key in seatData.countMap)
        {
            var pai = Number.parseInt(key);
            if (pai != caishenPai && seatData.countMap[pai] == 2)
            {
                ++pairCnt;
            }
        }
    }

    //1张财神
    if (seatData.countMap[caishenPai]==1 && pairCnt==7)
    {
        return 1;
    }
    if (seatData.countMap[caishenPai]==2 && pairCnt==7)
    {
        return 2;
    }
    if (seatData.countMap[caishenPai]==2 && pairCnt==6)
    {
        return 1;    
    }
    if (seatData.countMap[caishenPai]==3 && pairCnt==6)
    {
        return 1;
    }
    if (seatData.countMap[caishenPai]==3 && pairCnt==5)
    {
        return 1;
    }
    return 0;
}

//有财神有白板情况下是否8对,0--没8对，1--软8对，2--硬8对
function is8PairsWithCaiShenBaiBan(seatData)
{
    if (seatData.countMap[caishenPai]<=0 || seatData.countMap[baibanPai]==null || caishenPai == baibanPai)
    {
        console.log("ErrMsg: data is wrong, no caishen");
        console.log(caishenPai);
        console.log(baibanPai);
        return -1;
    }
    var pairCnt = 0;
    var baibanCnt = 0; 
    if (seatData.holds.length==16)
    {
        for (var key in seatData.countMap)
        {
            var pai = Number.parseInt(key);
            if (pai==baibanPai)
            {
                ++baibanCnt;
            }
        }
        for (var key in seatData.countMap)
        {
            var pai = Number.parseInt(key);
            if (seatData.countMap[pai]==2)
            {
                ++pairCnt;
            }
        }
    }

    //1张财神
    if (seatData.countMap[caishenPai]==1 && pairCnt==7)
    {
        return 1;
    }
    if (seatData.countMap[caishenPai]==1 && baibanCnt>=2 && pairCnt==6)
    {
        return 1;
    }
    if (seatData.countMap[caishenPai]==2 && pairCnt==6)
    {
        return 1;    
    }
    if (seatData.countMap[caishenPai]==2 && pairCnt==7)
    {
        return 2;
    }
    if (seatData.countMap[caishenPai]==3 && pairCnt==6)
    {
        return 1;
    }
    if (seatData.countMap[caishenPai]==3 && pairCnt==5)
    {
        return 1;
    }

    return 0;
}

//吃牌中是否有财神
function checkChiHasCaiShen(seatData)
{
    if (seatData.chis.length<=0)
    {
        return 0;
    }
    var caishenCnt = 0;
    for (var i=0; i<seatData.chis.length; ++i)
    {
        if (seatData.chis[i]==caishenPai)
        {
            ++caishenCnt;
        }
    }
    return caishenCnt;
}

//碰牌中是否有财神
function checkPengHasCaiShen(seatData)
{
    if (seatData.pengs.length<=0)
    {
        return 0;
    }
    var caishenCnt = 0;
    for (var i=0; i<seatData.pengs.length; ++i)
    {
        if (seatData.pengs[i]==caishenPai)
        {
            ++caishenCnt;
        }
    }
    return caishenCnt;
}

//杠牌中是否有财神
function checkGangHasCaiShen(seatData)
{
    if ((seatData.diangangs.length+seatData.wangangs.length+seatData.angangs.length)<=0)
    {
        return 0;
    }
    var caishenCnt = 0;
    for (var i=0; i<seatData.diangangs.length; ++i)
    {
        if (seatData.diangangs[i]==caishenPai)
        {
            ++caishenCnt;
        }
    }
    for (var i=0; i<seatData.wangangs.length; ++i)
    {
        if (seatData.wangangs[i]==caishenPai)
        {
            ++caishenCnt;
        }
    }
        for (var i=0; i<seatData.angangs.length; ++i)
    {
        if (seatData.angangs[i]==caishenPai)
        {
            ++caishenCnt;
        }
    }
    return caishenCnt;
}

function getSeatIndex(userId){
    var seatIndex = roomMgr.getUserSeat(userId);
    if(seatIndex == null){
        return null;
    }
    return seatIndex;
}

function getGameByUserID(userId){
    var roomId = roomMgr.getUserRoom(userId);
    if(roomId == null){
        return null;
    }
    var game = games[roomId];
    return game;
}
//是否能操作
function hasOperations(seatData)
{
    if(seatData.canGang || seatData.canPeng || seatData.canHu || seatData.canChi)
    {
        return true;
    }
    return false;
}
//发送操作
function sendOperations(game,seatData,pai) 
{
    if(hasOperations(seatData))
    {
        if(pai == -1)
        {
            pai = seatData.holds[seatData.holds.length - 1];
        }

        //进行操作
        var data = {pai:pai, chi:seatData.canChi, hu:seatData.canHu, peng:seatData.canPeng, 
                    gang:seatData.canGang, gangpai:seatData.gangPai, chipai:seatData.canchis};
        userMgr.sendMsg(seatData.userId,'game_action_push',data);
    }
    else
    {
        userMgr.sendMsg(seatData.userId,'game_action_push');
    }
}

function moveToNextUser(game,nextSeat)
{
    game.fangpaoshumu = 0;
    //找到下一个没有和牌的玩家
    if(nextSeat == null){
        while(true){
            game.turn ++;
            game.turn %= 4;
            var turnSeat = game.gameSeats[game.turn];
            if(turnSeat.hued == false){
                return;
            }
        }
    }
    else{
        game.turn = nextSeat;
    }
}

function isSameType(type,arr){
    for(var i = 0; i < arr.length; ++i){
        var t = getMJType(arr[i]);
        if(type != -1 && type != t){
            return false;
        }
        type = t;
    }
    return true; 
}

function isZhongZhang(gameSeatData){
    var fn = function(arr){
        for(var i = 0; i < arr.length; ++i){
            var pai = arr[i];
            if(pai == 0 || pai == 8 || pai == 9 || pai == 17 || pai == 18 || pai == 26){
                return false;
            }
        }
        return true;
    }
    
    if(fn(gameSeatData.pengs) == false){
        return false;
    }
    if(fn(gameSeatData.angangs) == false){
        return false;
    }
    if(fn(gameSeatData.diangangs) == false){
        return false;
    }
    if(fn(gameSeatData.wangangs) == false){
        return false;
    }
    if(fn(gameSeatData.holds) == false){
        return false;
    }
    return true;
}

function isJiangDui(gameSeatData){
    var fn = function(arr){
        for(var i = 0; i < arr.length; ++i){
            var pai = arr[i];
            if(pai != 1 && pai != 4 && pai != 7
               && pai != 9 && pai != 13 && pai != 16
               && pai != 18 && pai != 21 && pai != 25
               ){
                return false;
            }
        }
        return true;
    }
    
    if(fn(gameSeatData.pengs) == false){
        return false;
    }
    if(fn(gameSeatData.angangs) == false){
        return false;
    }
    if(fn(gameSeatData.diangangs) == false){
        return false;
    }
    if(fn(gameSeatData.wangangs) == false){
        return false;
    }
    if(fn(gameSeatData.holds) == false){
        return false;
    }
    return true;
}

function isTinged(seatData)
{
    for(var k in seatData.tingMap){
        return true;
    }
    return false;
}

function computeFanScore(game,fan){
    if(fan > game.conf.maxFan){
        fan = game.conf.maxFan;
    }
    return (1 << fan) * game.conf.baseScore;
}

function findUnTingedPlayers(game){
    var arr = [];
    for(var i = 0; i < game.gameSeats.length; ++i){
        var ts = game.gameSeats[i];
        //如果没有胡，且没有听牌
        if(!ts.hued && !isTinged(ts)){
            arr.push(i);
            recordUserAction(game,ts,"beichadajiao",-1);
        }
    }
    return arr;
}

function calculateResult(game,roomInfo){
    
    var baseScore = game.conf.baseScore;
    var numOfHued = 0;
    for(var i = 0; i < game.gameSeats.length; ++i){
        if(game.gameSeats[i].hued == true){
            numOfHued++;
        }
    }
    
    for(var i = 0; i < game.gameSeats.length; ++i){
        var sd = game.gameSeats[i];
        console.log(sd.actions);
        //对所有胡牌的玩家进行统计
        if(isTinged(sd)){
            //统计自己的番子和分数
            //基础番(平胡0番，对对胡1番、七对2番) + 清一色2番 + 杠+1番
            //杠上花+1番，杠上炮+1番 抢杠胡+1番，金钩胡+1番，海底胡+1番
            var fan = sd.fan;
            //if(isQingYiSe(sd)){
                //sd.qingyise = true;
                //fan += 2;
            //}
            
            var numOfGangs = sd.diangangs.length + sd.wangangs.length + sd.angangs.length;
            for(var j = 0; j < sd.pengs.length; ++j){
                var pai = sd.pengs[j];
                if(sd.countMap[pai] == 1){
                    numOfGangs++;
                }
            }
            for(var k in sd.countMap){
                if(sd.countMap[k] == 4){
                    numOfGangs++;
                }
            }
            sd.numofgen = numOfGangs;
            
            //金钩胡
            if(sd.holds.length == 1 || sd.holds.length == 2){
                fan += 1;
                sd.isJinGouHu = true;
            }
            
            if(sd.isHaiDiHu){
                fan += 1;
            }
            
            if(game.conf.tiandihu){
                if(sd.isTianHu){
                    fan += 3;
                }
                else if(sd.isDiHu){
                    fan += 2;
                }
            }
            
            var isjiangdui = false;
            if(game.conf.jiangdui){
                if(sd.pattern == "7pairs"){
                    if(sd.numofgen > 0){
                        sd.numofgen -= 1;
                        sd.pattern == "l7pairs";
                        isjiangdui = isJiangDui(sd);
                        if(isjiangdui){
                            sd.pattern == "j7paris";
                            fan += 2;    
                        }   
                        else{
                            fan += 1;
                        }
                    }
                }
                else if(sd.pattern == "duidui"){
                    isjiangdui = isJiangDui(sd);
                    if(isjiangdui){
                        sd.pattern = "jiangdui";
                        fan += 2;   
                    }
                }   
            }
            
            if(game.conf.menqing){
                //不是将对，才检查中张
                if(!isjiangdui){
                    sd.isZhongZhang = isZhongZhang(sd);
                    if(sd.isZhongZhang){
                        fan += 1;
                    }                
                }
                
                sd.isMenQing = isMenQing(sd);
                if(sd.isMenQing){
                    fan += 1;
                }                
            }
            
            fan += sd.numofgen;
            if(sd.isGangHu){
                fan += 1;
            }
            if(sd.isQiangGangHu){
                fan += 1;
            }

            //收杠钱
            var additonalscore = 0;
            for(var a = 0; a < sd.actions.length; ++a){
                var ac = sd.actions[a];
                if(ac.type == "fanggang"){
                    var ts = game.gameSeats[ac.targets[0]];
                    //检查放杠的情况，如果目标没有和牌，且没有叫牌，则不算 用于优化前端显示
                    if((ts.hued) == false && (isTinged(ts) == false)){
                        ac.state = "nop";
                    }
                }
                else if(ac.type == "angang" || ac.type == "wangang" || ac.type == "diangang"){
                    if(ac.state != "nop"){
                        var acscore = ac.score;
                        additonalscore += ac.targets.length * acscore * baseScore;
                        //扣掉目标方的分
                        for(var t = 0; t < ac.targets.length; ++t){
                            var six = ac.targets[t];
                            game.gameSeats[six].score -= acscore * baseScore;
                        }                   
                    }
                }
                else if(ac.type == "maozhuanyu"){
                    //对于呼叫转移，如果对方没有叫牌，表示不得行
                    if(isTinged(ac.owner)){
                        //如果
                        var ref = ac.ref;
                        var acscore = ref.score;
                        var total = ref.targets.length * acscore * baseScore;
                        additonalscore += total;
                        //扣掉目标方的分
                        if(ref.payTimes == 0){
                            for(var t = 0; t < ref.targets.length; ++t){
                                var six = ref.targets[t];
                                game.gameSeats[six].score -= acscore * baseScore;
                            }                            
                        }
                        else{
                            //如果已经被扣过一次了，则由杠牌这家赔
                            ac.owner.score -= total;
                        }
                        ref.payTimes++;
                        ac.owner = null;
                        ac.ref = null;
                    }
                }
                else if(ac.type == "zimo" || ac.type == "hu" || ac.type == "ganghua" || ac.type == "dianganghua" || ac.type == "gangpaohu" || ac.type == "qiangganghu" || ac.type == "chadajiao"){
                    var extraScore = 0;
                    if(ac.iszimo){
                        if(game.conf.zimo == 0){
                            //自摸加底
                            extraScore = baseScore;
                        }
                        if(game.conf.zimo == 1){
                            fan += 1;
                        }
                        else{
                            //nothing.
                        }
                    }
                    var score = computeFanScore(game,fan) + extraScore;
                    sd.score += score * ac.targets.length;

                    for(var t = 0; t < ac.targets.length; ++t){
                        var six = ac.targets[t];
                        game.gameSeats[six].score -= score;
                    }
                }
            }

            if(fan > game.conf.maxFan){
                fan = game.conf.maxFan;
            }
            //一定要用 += 。 因为此时的sd.score可能是负的
            sd.score += additonalscore;
            if(sd.pattern != null){
                sd.fan = fan;
            }
        }
        else{
            for(var a = sd.actions.length -1; a >= 0; --a){
                var ac = sd.actions[a];
                if(ac.type == "angang" || ac.type == "wangang" || ac.type == "diangang"){
                    //如果3家都胡牌，则需要结算。否则认为是查叫
                    if(numOfHued < 3){
                        sd.actions.splice(a,1);                        
                    }
                    else{
                        if(ac.state != "nop"){
                            var acscore = ac.score;
                            sd.score += ac.targets.length * acscore * baseScore;
                            //扣掉目标方的分
                            for(var t = 0; t < ac.targets.length; ++t){
                                var six = ac.targets[t];
                                game.gameSeats[six].score -= acscore * baseScore;
                            }                   
                        }   
                    }
                }
            }
        }
    }
}

function doGameOver(game,userId,forceEnd){
    var roomId = roomMgr.getUserRoom(userId);
    if(roomId == null){
        return;
    }
    var roomInfo = roomMgr.getRoom(roomId);
    if(roomInfo == null){
        return;
    }

    var results = [];
    var dbresult = [0,0,0,0];
    
    var fnNoticeResult = function(isEnd){
        userMgr.broadcastInRoom('game_over_push',{results:results,isend:isEnd},userId,true);
        //如果局数已够，则进行整体结算，并关闭房间
        if(isEnd){
            setTimeout(function(){
                if(roomInfo.numOfGames > 1){
                    store_history(roomInfo);    
                }
                
                userMgr.kickAllInRoom(roomId);
                roomMgr.destroy(roomId);
                db.archive_games(roomInfo.uuid);            
            },1500);
        }
    }

    if(game != null){
        if(!forceEnd){
            calculateResult(game,roomInfo);    
        }
       
        for(var i = 0; i < roomInfo.seats.length; ++i){
            var rs = roomInfo.seats[i];
            var sd = game.gameSeats[i];

            rs.ready = false;
            rs.score += sd.score

            results.push({
                userId:sd.userId,
                actions:sd.actions,
                pengs:sd.pengs,
                wangangs:sd.wangangs,
                diangangs:sd.diangangs,
                angangs:sd.angangs,
                numofgen:sd.numofgen,
                holds:sd.holds,
                fan:sd.fan,
                score:sd.score,
                totalscore:rs.score,
                qingyise:sd.qingyise,
                pattern:sd.pattern,
                isganghu:sd.isGangHu,
                menqing:sd.isMenQing,
                zhongzhang:sd.isZhongZhang,
                jingouhu:sd.isJinGouHu,
                haidihu:sd.isHaiDiHu,
                tianhu:sd.isTianHu,
                dihu:sd.isDiHu,
                huorder:game.hupaiList.indexOf(i)
            });

            dbresult[i] = sd.score;
            console.log(dbresult);
            console.log(results);
            delete gameSeatsOfUsers[sd.userId];
        }
        delete games[roomId];
        
        var old = roomInfo.nextButton;
        if(game.yipaoduoxiang >= 0){
            roomInfo.nextButton = game.yipaoduoxiang;
        }
        else if(game.firstHupai >= 0){
            roomInfo.nextButton = game.firstHupai;
        }
        else{
            roomInfo.nextButton = (game.turn + 1) % 4;
        }

        if(old != roomInfo.nextButton){
            db.update_next_button(roomId,roomInfo.nextButton);
        }
    }
    
    if(forceEnd || game == null){
        console.log("force game end");
        fnNoticeResult(true);   
    }
    else{
        //保存游戏
        store_game(game,function(ret){
            console.log("save the game result to db");
            db.update_game_result(roomInfo.uuid,game.gameIndex,dbresult);
            
            //记录打牌信息
            var str = JSON.stringify(game.actionList);
            db.update_game_action_records(roomInfo.uuid,game.gameIndex,str);
        
            //保存游戏局数
            db.update_num_of_turns(roomId,roomInfo.numOfGames);
            console.log("game round is ");
            console.log(roomInfo.numOfGames);
            //如果是第一次，并且不是强制解散 则扣除房卡
            if(roomInfo.numOfGames == 4){
                var cost = 2;
                if(roomInfo.conf.maxGames == 8){
                    cost = 3;
                }
                db.cost_gems(game.gameSeats[0].userId,cost);
            }

            var isEnd = (roomInfo.numOfGames >= roomInfo.conf.maxGames);
            console.log(isEnd);
            fnNoticeResult(isEnd);
        });   
    }
}

function recordUserAction(game,seatData,type,target){
    var d = {type:type,targets:[]};
    if(target != null){
        if(typeof(target) == 'number'){
            d.targets.push(target);    
        }
        else{
            d.targets = target;
        }
    }
    else{
        for(var i = 0; i < game.gameSeats.length; ++i){
            var s = game.gameSeats[i];
            if(i != seatData.seatIndex && s.hued == false){
                d.targets.push(i);
            }
        }        
    }

    seatData.actions.push(d);
    return d;
}

function recordGameAction(game,si,action,pai){
    game.actionList.push(si);
    game.actionList.push(action);
    if(pai != null){
        game.actionList.push(pai);
    }
}

exports.setReady = function(userId,callback){
    var roomId = roomMgr.getUserRoom(userId);
    if(roomId == null){
        return;
    }
    var roomInfo = roomMgr.getRoom(roomId);
    if(roomInfo == null){
        return;
    }

    roomMgr.setReady(userId,true);

    var game = games[roomId];
    if(game == null){
        if(roomInfo.seats.length == 4){
            for(var i = 0; i < roomInfo.seats.length; ++i){
                var s = roomInfo.seats[i];
                if(s.ready == false || userMgr.isOnline(s.userId)==false){
                    return;
                }
            }
            //4个人到齐了，并且都准备好了，则开始新的一局
            console.log("new round ");
            exports.begin(roomId);
        }
    }
    else{
        var numOfMJ = game.mahjongs.length - game.currentIndex;
        var remainingGames = roomInfo.conf.maxGames - roomInfo.numOfGames;

        var data = {
            state:game.state,
            numofmj:numOfMJ,
            button:game.button,
            turn:game.turn,
            chuPai:game.chuPai,
            caishen:caishenPai,
        };

        data.seats = [];
        var seatData = null;
        for(var i = 0; i < 4; ++i){
            var sd = game.gameSeats[i];
            console.log("game turn is " + game.turn);
            if (i==game.turn%4)
            {
                console.log("game turn is "+game.turn);
                game.canChuPai = true;
            }
            game.fengpaiList.length=0;
            findoutYouXianFengPai(game,sd);
            var s = {
                userid:sd.userId,
                folds:sd.folds,
                chis:sd.chis,
                angangs:sd.angangs,
                diangangs:sd.diangangs,
                wangangs:sd.wangangs,
                pengs:sd.pengs,
                youxian:sd.youxianChuFengPai,
                hued:sd.hued,
                iszimo:sd.iszimo,
            }
            if(sd.userId == userId){
                s.holds = sd.holds;
                seatData = sd;
            }
            data.seats.push(s);
        }

        //同步整个信息给客户端
        userMgr.sendMsg(userId,'game_sync_push',data);
        sendOperations(game,seatData,game.chuPai);
    }
}

function store_single_history(userId,history){
    db.get_user_history(userId,function(data){
        if(data == null){
            data = [];
        }
        while(data.length >= 10){
            data.shift();
        }
        data.push(history);
        db.update_user_history(userId,data);
    });
}

function store_history(roomInfo){
    var seats = roomInfo.seats;
    var history = {
        uuid:roomInfo.uuid,
        id:roomInfo.id,
        time:roomInfo.createTime,
        seats:new Array(4)
    };

    for(var i = 0; i < seats.length; ++i){
        var rs = seats[i];
        var hs = history.seats[i] = {};
        hs.userid = rs.userId;
        hs.name = rs.name;
        hs.score = rs.score;
    }

    for(var i = 0; i < seats.length; ++i){
        var s = seats[i];
        store_single_history(s.userId,history);
    }
}

function construct_game_base_info(game){
    var baseInfo = {
        type:game.conf.type,
        button:game.button,
        index:game.gameIndex,
        mahjongs:game.mahjongs,
        game_seats:new Array(4)
    }
    
    for(var i = 0; i < 4; ++i){
        baseInfo.game_seats[i] = game.gameSeats[i].holds;
    }
    game.baseInfoJson = JSON.stringify(baseInfo);
}

function store_game(game,callback){
    db.create_game(game.roomInfo.uuid,game.gameIndex,game.baseInfoJson,callback);
}

exports.chipai = function(userId,paiArr)
{
    console.log("follow is chupai data: ");
    console.log(paiArr);
    if (paiArr.length<=0)
    {
        console.log("receive data is wrong");
        return;
    }

    //座位数据
    var seatData = gameSeatsOfUsers[userId];
    if(seatData == null){
        console.log("can't find user game data.");
        return;
    }

    //如果是他出的牌，则忽略
    var game = seatData.game;
    if(game.turn == seatData.seatIndex){
        console.log("it's your turn.");
        return;
    }

    //如果没有吃的机会，则不能再吃
    if(seatData.canChi == false){
        console.log("seatData.chipai == false");
        return;
    }

    //和的了，就不要再来了
    if(seatData.hued){
        console.log('you have already hued. no kidding plz.');
        return;
    }

    //如果有人可以胡牌，则需要等待
    var i = game.turn;
    while(true){
        var i = (i + 1)%4;
        if(i == game.turn){
            break;
        }
        else{
            var seatInfo = game.gameSeats[i];
            if(seatInfo.canHu && i != seatData.seatIndex){
                return;    
            }
        }
    }

    //清空状态
    clearAllOptions(game);
    
    //进行吃牌处理，扣掉手上的牌
    var delPaiArr = [];
    var strpaiArr = "";
    strpaiArr = paiArr;
    strpaiArr = strpaiArr.substring(1,strpaiArr.length-1);
    var delPaiArr = strpaiArr.split(",");

    for(var i = 0; i < 3; ++i){
        var pai = Number.parseInt(delPaiArr[i]);
        if(delPaiArr < 0)
        {
            console.log("chipai: can't find mj.");
            return;
        }
        if (pai != game.chuPai)
        {
            var index = seatData.holds.indexOf(pai);
            if (index == -1)
            {
                console.log("ErrMsg: not find holds chi pai!");
                continue;
            }
            seatData.holds.splice(index,1);
            --seatData.countMap[pai];
        }
    }
    game.chuPai = -1;
    for (var i=0; i<3; ++i)
    {
        seatData.chis.push(delPaiArr[i]);
    }

    //记录
    recordGameAction(game,seatData.seatIndex,ACTION_CHI,pai);

    //广播通知其它玩家
    userMgr.broadcastInRoom('chi_notify_push',{userid:seatData.userId,chipaiarr:delPaiArr},seatData.userId,true);

    //下一家
    moveToNextUser(game,seatData.seatIndex);
    
    //广播通知玩家出牌方
    seatData.canChuPai = true;
    userMgr.broadcastInRoom('game_chupai_push',seatData.userId,seatData.userId,true);
};

exports.peng = function(userId)
{
    console.log("follow is pengpai data (userId):");
    console.log(userId);
    var seatData = gameSeatsOfUsers[userId];
    if(seatData == null)
    {
        console.log("can't find user game data.");
        return;
    }

    //如果是他出的牌，则忽略
    var game = seatData.game;
    if(game.turn == seatData.seatIndex){
        console.log("it's your turn.");
        return;
    }

    //如果没有碰的机会，则不能再碰
    if(seatData.canPeng == false){
        console.log("seatData.peng == false");
        return;
    }

    //和的了，就不要再来了
    if(seatData.hued){
        console.log('you have already hued. no kidding plz.');
        return;
    }
    
    //如果有人可以胡牌，则需要等待
    var i = game.turn;
    while(true){
        var i = (i + 1)%4;
        if(i == game.turn){
            break;
        }
        else{
            var ddd = game.gameSeats[i];
            if(ddd.canHu && i != seatData.seatIndex)
            {
                console.log("need wait other hupai ");
                return;    
            }
        }
    }

    //清空状态
    clearAllOptions(game);

    //验证手上的牌的数目
    var pai = game.chuPai;
    var c = seatData.countMap[pai];
    if(c == null || c < 2)
    {
        console.log("pai:" + pai + ",count:" + c);
        console.log(seatData.holds);
        console.log("lack of mj.");
        return;
    }

    //进行碰牌处理,扣掉手上的牌
    for(var i = 0; i < 2; ++i)
    {
        var index = seatData.holds.indexOf(pai);
        if(index == -1){
            console.log(seatData.holds);
            console.log("peng: can't find mj.");
            return;
        }
        seatData.holds.splice(index,1);
        seatData.countMap[pai]--;
    }
    seatData.pengs.push(pai);
    game.chuPai = -1;

    //记录数据
    recordGameAction(game,seatData.seatIndex,ACTION_PENG,pai);

    //广播通知其它玩家
    userMgr.broadcastInRoom('peng_notify_push',{userid:seatData.userId,pai:pai},seatData.userId,true);

    //碰的玩家打牌
    moveToNextUser(game,seatData.seatIndex);
    
    //广播通知玩家出牌方
    seatData.canChuPai = true;
    userMgr.broadcastInRoom('game_chupai_push',seatData.userId,seatData.userId,true);
};

exports.isPlaying = function(userId)
{
    var seatData = gameSeatsOfUsers[userId];
    if(seatData == null)
    {
        return false;
    }

    var game = seatData.game;  
    if(game.state == "idle")
    {
        return false;
    }
    return true;
}

//是否能抢杠
function checkCanQiangGang(game,turnSeat,seatData,pai)
{
    var hasActions = false;
    for(var i = 0; i < game.gameSeats.length; ++i)
    {
        //杠牌者不检查
        if(seatData.seatIndex == i)
        {
            continue;
        }

        //已经和牌的不再检查
        var seatInfo = game.gameSeats[i];
        if(seatInfo.hued)
        {
            continue;
        }

        //检查是否能胡
        checkCanHu(game,seatInfo,pai);
        if(seatInfo.canHu)
        {
            sendOperations(game,seatInfo,pai);
            hasActions = true;
        }
    }
    if(hasActions)
    {
        game.qiangGangContext = 
        {
            turnSeat:turnSeat,
            seatData:seatData,
            pai:pai,
            isValid:true,
        }
    }
    else
    {
        game.qiangGangContext = null;
    }
    return game.qiangGangContext != null;
}

function operGang(game,turnSeat,seatData,gangtype,numOfCnt,pai)
{
    var seatIndex = seatData.seatIndex;
    var gameTurn = turnSeat.seatIndex;
    var isZhuanShouGang = false;
    if(gangtype == "wangang")
    {
        var idx = seatData.pengs.indexOf(pai);
        if(idx >= 0)
        {
            seatData.pengs.splice(idx,1);
        }
        
        //如果最后一张牌不是杠的牌，则认为是转手杠
        if(seatData.holds[seatData.holds.length - 1] != pai)
        {
            isZhuanShouGang = true;
        }
    }

    //进行杠牌处理,扣掉手上的牌
    for(var i = 0; i < numOfCnt; ++i)
    {
        var index = seatData.holds.indexOf(pai);
        if(index == -1)
        {
            console.log(seatData.holds);
            console.log("gang: can't find mj.");
            return;
        }
        seatData.holds.splice(index,1);
        seatData.countMap[pai]--;
    }
    recordGameAction(game,seatData.seatIndex,ACTION_GANG,pai);

    //记录下玩家的杠牌
    if(gangtype == "angang")
    {
        seatData.angangs.push(pai);
        var ac = recordUserAction(game,seatData,"angang");
        ac.score = game.conf.baseScore*2;
    }
    else if(gangtype == "diangang")
    {
        seatData.diangangs.push(pai);
        var ac = recordUserAction(game,seatData,"diangang",gameTurn);
        ac.score = game.conf.baseScore*2;
        var fs = turnSeat;
        recordUserAction(game,fs,"fanggang",seatIndex);
    }
    else if(gangtype == "wangang")
    {
        seatData.wangangs.push(pai);
        if(isZhuanShouGang == false)
        {
            var ac = recordUserAction(game,seatData,"wangang");
            ac.score = game.conf.baseScore;            
        }
        else
        {
            recordUserAction(game,seatData,"zhuanshougang");
        }
    }

    checkCanTingPai(game,seatData);

    //通知其他玩家，有人杠了牌
    userMgr.broadcastInRoom('gang_notify_push',{userid:seatData.userId,pai:pai,gangtype:gangtype},seatData.userId,true);

    //变成自己的轮子
    moveToNextUser(game,seatIndex);

    //再次摸牌
    nextUserMoPai(game);   
    
    //只能放在这里。因为过手就会清除杠牌标记
    seatData.lastFangGangSeat = gameTurn;
}

//杠操作
exports.gang = function(userId,pai)
{
    console.log("follow is gangpai data: (userId,pai)");
    console.log(userId);
    console.log(pai);
    var seatData = gameSeatsOfUsers[userId];
    if(seatData == null)
    {
        console.log("can't find user game data.");
        return;
    }
    var seatIndex = seatData.seatIndex;
    var game = seatData.game;

    //如果没有杠的机会，则不能再杠
    if(seatData.canGang == false) 
    {
        console.log("seatData.gang == false");
        return;
    }

    //和的了，就不要再来了
    if(seatData.hued)
    {
        console.log('you have already hued. no kidding plz.');
        return;
    }

    //没数据不能杠
    if(seatData.gangPai.indexOf(pai) == -1)
    {
        console.log("the given pai can't be ganged.");
        return;   
    }
    
    //如果有人可以胡牌，则需要等待
    var i = game.turn;
    while(true)
    {
        var i = (i + 1)%4;
        if(i == game.turn)
        {
            break;
        }
        else
        {
            var seatInfo = game.gameSeats[i];
            if(seatInfo.canHu && i != seatData.seatIndex)
            {
                console.log("need wait,you can not gang until other is hued.");
                return;    
            }
        }
    }

    //判断杠的类型
    var numOfCnt = seatData.countMap[pai];
    var gangtype = "";
    if(numOfCnt == 1)   //弯杠,因为去掉了碰牌
    {
        gangtype = "wangang";
    }
    else if(numOfCnt == 3)
    {
        gangtype = "diangang";
    }
    else if(numOfCnt == 4)
    {
        gangtype = "angang";
    }
    else
    {
        console.log("invalid pai count.");
        return;
    }
    
    //杠牌后清状态并广播
    game.chuPai = -1;
    clearAllOptions(game);
    seatData.canChuPai = false;
    userMgr.broadcastInRoom('hangang_notify_push',seatIndex,seatData.userId,true);
    
    //如果是弯杠，则需要检查是否可以抢杠
    var turnSeat = game.gameSeats[game.turn];
    if(numOfCnt == 1)
    {
        var canQiangGang = checkCanQiangGang(game,turnSeat,seatData,pai);
        if(canQiangGang)
        {
            console.log("need wait,other need qianggang.");
            return;
        }
    }
    
    //杠牌
    operGang(game,turnSeat,seatData,gangtype,numOfCnt,pai);
};

exports.hu = function(userId)
{
    console.log("follow is hupai data: (userId)");
    console.log(userId);
    var seatData = gameSeatsOfUsers[userId];
    if(seatData == null)
    {
        console.log("can't find user game data.");
        return;
    }

    var seatIndex = seatData.seatIndex;
    var game = seatData.game;

    //如果他不能和牌，那和个啥啊
    if(seatData.canHu == false)
    {
        console.log("invalid request.");
        return;
    }

    //和的了，就不要再来了
    if(seatData.hued)
    {
        console.log('you have already hued. no kidding plz.');
        return;
    }

    //标记为和牌
    seatData.hued = true;
    var hupai = game.chuPai;
    var isZimo = false;

    var turnSeat = game.gameSeats[game.turn];
    seatData.isGangHu = turnSeat.lastFangGangSeat >= 0;
    var notify = -1;
    
    if(game.qiangGangContext != null)
    {
        var gangSeat = game.qiangGangContext.seatData;
        hupai = game.qiangGangContext.pai;
        notify = hupai;
        var ac = recordUserAction(game,seatData,"qiangganghu",gangSeat.seatIndex);    
        ac.iszimo = false;
        recordGameAction(game,seatIndex,ACTION_HU,hupai);
        seatData.isQiangGangHu = true;
        game.qiangGangContext.isValid = false;
        
        
        var idx = gangSeat.holds.indexOf(hupai);
        if(idx != -1){
            gangSeat.holds.splice(idx,1);
            gangSeat.countMap[hupai]--;
            userMgr.sendMsg(gangSeat.userId,'game_holds_push',gangSeat.holds);
        }
        //将牌添加到玩家的手牌列表，供前端显示
        seatData.holds.push(hupai);
        if(seatData.countMap[hupai]){
            seatData.countMap[hupai]++;
        }
        else{
            seatData.countMap[hupai] = 1;
        }
        
        recordUserAction(game,gangSeat,"beiqianggang",seatIndex);
    }
    else if(game.chuPai == -1)
    {
        hupai = seatData.holds[seatData.holds.length - 1];
        notify = -1;
        if(seatData.isGangHu)
        {
            if(turnSeat.lastFangGangSeat == seatIndex)
            {
                var ac = recordUserAction(game,seatData,"ganghua");    
                ac.iszimo = true;
            }
            else
            {
                var diangganghua_zimo = game.conf.dianganghua == 1;
                if(diangganghua_zimo)
                {
                    var ac = recordUserAction(game,seatData,"dianganghua");
                    ac.iszimo = true;
                }
                else
                {
                    var ac = recordUserAction(game,seatData,"dianganghua",turnSeat.lastFangGangSeat);
                    ac.iszimo = false;
                }
            }
        }
        else
        {
            var ac = recordUserAction(game,seatData,"zimo");
            ac.iszimo = true;
        }

        isZimo = true;
        recordGameAction(game,seatIndex,ACTION_ZIMO,hupai);
    }
    else
    {
        notify = game.chuPai;
        //将牌添加到玩家的手牌列表，供前端显示
        seatData.holds.push(game.chuPai);
        if(seatData.countMap[game.chuPai])
        {
            seatData.countMap[game.chuPai]++;
        }
        else{
            seatData.countMap[game.chuPai] = 1;
        }

        console.log(seatData.holds);

        var at = "hu";
        //炮胡
        if(turnSeat.lastFangGangSeat >= 0)
        {
            at = "gangpaohu";
        }

        var ac = recordUserAction(game,seatData,at,game.turn);
        ac.iszimo = false;

        //毛转雨
        if(turnSeat.lastFangGangSeat >= 0){
            for(var i = turnSeat.actions.length-1; i >= 0; --i){
                var t = turnSeat.actions[i];
                if(t.type == "diangang" || t.type == "wangang" || t.type == "angang"){
                    t.state = "nop";
                    t.payTimes = 0;

                    var nac = {
                        type:"maozhuanyu",
                        owner:turnSeat,
                        ref:t
                    }
                    seatData.actions.push(nac);
                    break;
                }
            }
        }

        //记录玩家放炮信息
        var fs = game.gameSeats[game.turn];
        recordUserAction(game,fs,"fangpao",seatIndex);

        recordGameAction(game,seatIndex,ACTION_HU,hupai);

        game.fangpaoshumu++;

        if(game.fangpaoshumu > 1){
            game.yipaoduoxiang = seatIndex;
        }
    }

    if(game.firstHupai < 0){
        game.firstHupai = seatIndex;
    }

    //保存番数
    var ti = seatData.tingMap[hupai];
    //seatData.fan = ti.fan;
    //seatData.pattern = ti.pattern;
    seatData.iszimo = isZimo;
    //如果是最后一张牌，则认为是海底胡
    seatData.isHaiDiHu = game.currentIndex == game.mahjongs.length;
    game.hupaiList.push(seatData.seatIndex);
    
    if(game.conf.tiandihu){
        if(game.chupaiCnt == 0 && game.button == seatData.seatIndex && game.chuPai == -1){
            seatData.isTianHu = true;
        }
        else if(game.chupaiCnt == 1 && game.turn == game.button && game.button != seatData.seatIndex && game.chuPai != -1){
            seatData.isDiHu = true;   
        }   
    }

    clearAllOptions(game,seatData);

    //通知前端，有人和牌了
    userMgr.broadcastInRoom('hu_push',{seatindex:seatIndex,iszimo:isZimo,hupai:notify},seatData.userId,true);
    
    //
    if(game.lastHuPaiSeat == -1){
        game.lastHuPaiSeat = seatIndex;
    }
    else{
        var lp = (game.lastFangGangSeat - game.turn + 4) % 4;
        var cur = (seatData.seatIndex - game.turn + 4) % 4;
        if(cur > lp){
            game.lastHuPaiSeat = seatData.seatIndex;
        }
    }

    //如果只有一家没有胡，则结束
    //var numOfHued = 0;
    //for(var i = 0; i < game.gameSeats.length; ++i){
        //var ddd = game.gameSeats[i];
        //if(ddd.hued){
            //numOfHued ++;
        //}
    //}
    //和了三家
    //if(numOfHued == 3){
        //doGameOver(game,seatData.userId);
        //return;
    //}

    //清空所有非胡牌操作
    for(var i = 0; i < game.gameSeats.length; ++i){
        var ddd = game.gameSeats[i];
        ddd.canPeng = false;
        ddd.canGang = false;
        ddd.canChuPai = false;
        sendOperations(game,ddd,hupai);
    }

    //如果还有人可以胡牌，则等待
    //for(var i = 0; i < game.gameSeats.length; ++i){
        //var ddd = game.gameSeats[i];
        //if(ddd.canHu){
            //return;
        //}
    //}

    //牌局结束
    doGameOver(game,seatData.userId);
    
    //和牌的下家继续打
    //clearAllOptions(game);
    //game.turn = game.lastHuPaiSeat;
    //moveToNextUser(game);
    //nextUserMoPai(game);
};

//弃牌
exports.guo = function(userId)
{
    var seatData = gameSeatsOfUsers[userId];
    if(seatData == null){
        console.log("can't find user game data.");
        return;
    }

    var seatIndex = seatData.seatIndex;
    var game = seatData.game;

    //如果玩家没有对应的操作，则也认为是非法消息
    if((seatData.canGang || seatData.canPeng || seatData.canHu || seatData.canChi) == false)
    {
        console.log("no need guo.");
        return;
    }

    //如果是玩家自己的轮子，不是接牌，则不需要额外操作
    var doNothing = game.chuPai == -1 && game.turn == seatIndex;

    userMgr.sendMsg(seatData.userId,"guo_result");
    clearAllOptions(game,seatData);
    
    //这里还要处理过胡的情况
    if(game.chuPai >= 0 && seatData.canHu)
    {
        seatData.guoHuFan = seatData.tingMap[game.chuPai].fan;
    }

    if(doNothing)
    {
        return;
    }
    
    //如果还有人可以操作，则等待
    for(var i = 0; i < game.gameSeats.length; ++i)
    {
        var seatInfo = game.gameSeats[i];
        if(hasOperations(seatInfo)){
            return;
        }
    }

    //如果是已打出的牌，则需要通知。
    if(game.chuPai >= 0){
        var uid = game.gameSeats[game.turn].userId;
        userMgr.broadcastInRoom('guo_notify_push',{userId:uid,pai:game.chuPai},seatData.userId,true);
        seatData.folds.push(game.chuPai);
        game.chuPai = -1;
    }
    
    
    var qiangGangContext = game.qiangGangContext;
    //清除所有的操作
    clearAllOptions(game);
    
    if(qiangGangContext != null && qiangGangContext.isValid)
    {
        operGang(game,qiangGangContext.turnSeat,qiangGangContext.seatData,"wangang",1,qiangGangContext.pai);        
    }
    else
    {
        //下家摸牌
        moveToNextUser(game);
        nextUserMoPai(game);   
    }
};

exports.hasBegan = function(roomId){
    var game = games[roomId];
    if(game != null){
        return true;
    }
    var roomInfo = roomMgr.getRoom(roomId);
    if(roomInfo != null){
        return roomInfo.numOfGames > 0;
    }
    return false;
};

exports.doDissolve = function(roomId){
    var roomInfo = roomMgr.getRoom(roomId);
    if(roomInfo == null){
        return null;
    }

    var game = games[roomId];
    doGameOver(game,roomInfo.seats[0].userId,true);
};

exports.dissolveRequest = function(roomId,userId){
    var roomInfo = roomMgr.getRoom(roomId);
    if(roomInfo == null){
        return null;
    }

    if(roomInfo.dr != null){
        return null;
    }

    var seatIndex = roomMgr.getUserSeat(userId);
    if(seatIndex == null){
        return null;
    }

    roomInfo.dr = {
        endTime:Date.now() + 30000,
        states:[false,false,false,false]
    };
    roomInfo.dr.states[seatIndex] = true;

    dissolvingList.push(roomId);

    return roomInfo;
};

exports.dissolveAgree = function(roomId,userId,agree){
    var roomInfo = roomMgr.getRoom(roomId);
    if(roomInfo == null){
        return null;
    }

    if(roomInfo.dr == null){
        return null;
    }

    var seatIndex = roomMgr.getUserSeat(userId);
    if(seatIndex == null){
        return null;
    }

    if(agree){
        roomInfo.dr.states[seatIndex] = true;
    }
    else{
        roomInfo.dr = null;
        var idx = dissolvingList.indexOf(roomId);
        if(idx != -1){
            dissolvingList.splice(idx,1);           
        }
    }
    return roomInfo;
};



function update() {
    for(var i = dissolvingList.length - 1; i >= 0; --i){
        var roomId = dissolvingList[i];
        
        var roomInfo = roomMgr.getRoom(roomId);
        if(roomInfo != null && roomInfo.dr != null){
            if(Date.now() > roomInfo.dr.endTime){
                console.log("delete room and games");
                exports.doDissolve(roomId);
                dissolvingList.splice(i,1); 
            }
        }
        else{
            dissolvingList.splice(i,1);
        }
    }
}

setInterval(update,1000);